//
//  NSObject+ZXRuntime.h
//  CollagePicture
//
//  Created by simon on 16/11/17.
//  Copyright © 2016年 simon. All rights reserved.
//
//  简介：这是种Hook技术，利用OC的Runtime特性，动态改变SEL（方法名）和IMP（方法实现）的对应关系。，runtime中常用的API有动态替换方法，交换方法实现，设置新实现函数；一般在类或分类的+load方法中实现hook。
//  2018.01.10 添加注释
//  2019.12.30 优化代码

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

NS_ASSUME_NONNULL_BEGIN



//动态添加方法Method（由SEL+IMP+type组成）：class_addMethod的实现会覆盖父类的IMP，但不会取代本类中已存在的IMP，如果本类中已包含一个同SEL对应的IMP，则函数会返回NO。如果要更改已存在IMP，可以使用method_setImplementation。即：动态添加的SEL方法不能在该类中有相同SEL的IMP，不然会无法添加成功；不管是自定义方法还是系统方法。
static inline BOOL af_addMethod(Class theClass, SEL _Nonnull selector, Method _Nonnull method) {
    return class_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));
}



@interface NSObject (ZXRuntime)


/// 交换二个实例方法的IMP（方法实现），这2个SEL必须要有方法实现；
/// @param originalSel 原方法SEL，有可能是本类或者父类的；
/// @param swizzledSel 目标新方法SEL，本类；
///  如果originalSelector为父类方法,而本类没有,直接交换带来的后果就是影响了所有父类的事例对象的方法实现,后果就不可控了.
///  class_addMethod：如果本类没有SEL，则能添加成功,直接给本类添加SEL+交换的新IMP；
///  class_replaceMethod：把交换的SEL替换为原始SEL的IMP；
+ (void)zx_exchangeInstanceMethodWithOriginalSelector:(SEL)originalSel
                                     swizzledSelector:(SEL)swizzledSel;


/// 交换二个类方法的IMP（方法实现）
/// @param originalSel 原方法SEL，有可能本类或者父类的；
/// @param swizzledSel 目标新方法SEL，本类的；
+ (void)zx_exchangeClassMethodWithOriginalSelector:(SEL)originalSel
                                  swizzledSelector:(SEL)swizzledSel;



/// 获取指定类的所有实例方法列表（包括私有方法），不会获取父类中的;编译器会对属性自动实现Setter/Getter方法；
/// @param log 是否打印
/// @param block 获取到的sel方法回调
+ (void)getInstanceMethodListWithLog:(BOOL)log block:(void(^)(SEL sel))block;

/// 获取指定类的所有类方法列表（包括私有方法），不会获取父类中的;
/// @param log 是否打印
/// @param block 获取到的sel方法回调
+ (void)getClassMethodListWithLog:(BOOL)log block:(void(^)(SEL sel))block;


/// 获取指定类的所有实例变量列表（包括私有实例变量private修饰的,属性自动生成的_ivar）
/// @param block block description
+ (void)getClassIvarListWithBlock:(void(^)(Ivar ivar))block;


/// 取指定类的所有属性列表（包括私有扩展上的属性）
/// @param block block description
+ (void)getClassPropertyListWithBlock:(void(^)(objc_property_t property))block;

@end

NS_ASSUME_NONNULL_END

/*
 方法交互的初衷目的是替换原生的方法实现；自己添加自己的额外业务逻辑，看MJRefresh例子
 调用reloadData的时候，被交换了实现；则调用mj_reloadData；
 再调用mj_reloadData的时候，也被交换了实现；则调用reloadData；
 
 @implementation UITableView (MJRefresh)
 
 + (void)load
 {
    [self zx_exchangeInstanceMethodWithOriginalSelector:@selector(reloadData) swizzledSelector:@selector(mj_reloadData)];
 }
 
 - (void)mj_reloadData
 {
    [self mj_reloadData];
 
    [self executeReloadDataBlock];
 }
 @end
 
 */

